#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include<stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define EXIT_CODE 44    
#define LEFT "["
#define RIGHT "]"
#define LABEL "$"
#define SIZE 128
#define ARGC_SIZE 32
#define DIR_IN 0
#define DIR_OUT 1
#define DIR_APPEND 2
char Pwd[SIZE];
char CommandLine[SIZE]; // 用来存放获取到的命令行
char *argv[ARGC_SIZE];  // 用来存放分割好的子串
const char *delim = " ";
char *filename=NULL;
int dir;      // 重定向类型
int lastcode; // 最后一个程序退出码
char *GetUser()
{
    return getenv("USER");
}
char *GetHostname()
{
    return getenv("HOSTNAME");
}
char *GetPwd()
{
    return getcwd(Pwd, SIZE);
}
void Redirection()
{
    char *pos = CommandLine;
    while (*pos)
    {
        if (*pos == '>')
        {
            if (*(pos + 1) == '>')
            {
                // ls -a -l >> filename.txt //追加重定向
                *pos++ = '\0';
                *pos++ = '\0';
                filename = pos;
                dir = DIR_APPEND;
            }
            else
            {
                *pos++ = '\0';
                filename = pos;
                dir = DIR_OUT;
            }
        }
        else if (*pos == '<')
        {
            *pos++ = '\0';
            filename = pos;
            dir = DIR_IN;
        }
        else
        {
            pos++;
        }
    }
}
bool CommandInteraction()
{
    printf(LEFT "%s@%s %s" RIGHT LABEL, GetUser(), GetHostname(), GetPwd());
    if (fgets(CommandLine, SIZE, stdin))
    {
        // 获取成功
        CommandLine[strlen(CommandLine) - 1] = '\0';
        //"ls -a -l >/</>> filename.txt
        // 判断将来是否要重定向，如果要把文件记录好，把输入输出重定向记录好，将来执行命令时直接重定向到文件就行
        Redirection();
        return true;
    }
    return false;
}
int ParsingString()
{
    int pos = 0;
    argv[pos++] = strtok(CommandLine, delim);
    while (argv[pos++] = strtok(NULL, delim))
        ;
    return pos - 1;
}
bool isBuiltCommand(char *argv[], int argc)
{
    if (argc == 2 && strcmp(argv[0], "cd") == 0)
    {
        // 如果是内建命令直接执行
        chdir(argv[1]);                    // 更改工作路径
        GetPwd();                          // 获取当前路径到pwd
        sprintf(getenv("PWD"), "%s", Pwd); // 写入到环境变量
        return true;
    }
    else if (argc == 2 && strcmp(argv[0], "export") == 0)
    {
        putenv(argv[1]);
        return true;
    }
    else if (argc == 2 && strcmp(argv[0], "echo") == 0)
    {
        //"echo $?"
        if (strcmp(argv[1], "$?") == 0)
        {
            printf("%d\n", lastcode);
        }
        else if (*argv[1] == '$')
        {
            char *env = getenv(argv[1] + 1);
            printf("%s\n", env);
        }
        else
        {
            printf("%s\n", argv[1]);
        }
        return true;
    }

    // 特殊处理
    if (strcmp(argv[0], "ls")==0)
    {
        argv[argc++] = "--color";
        argv[argc] = NULL;
    }
    return false;
}
void normalCommand()
{
    // 创建子进程执行命令
    int id = fork();
    if (id == 0)
    {
        // 子进程
        // 判断是否需要重定向
        int fd=0;
        if (dir == DIR_IN)
        {
             fd = open(filename, O_RDONLY);
            dup2(fd,0);
        }
        else if(dir==DIR_OUT)
        {
            fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            dup2(fd,1);
        }
        else 
        {
            fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
            dup2(fd,1);
        }
        execvp(argv[0],argv);
        exit(EXIT_CODE);
    }
    else if (id > 0)
    {
        // 父进程
        int status;
        pid_t n = waitpid(id, &status, 0);
        if (n == id)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
    else
    {
        perror("fork");
        return;
    }
}
int main()
{
    while (1)
    {
        filename=NULL;
        dir=-1;
        // 1.获取，命令行交互
        bool n = CommandInteraction();
        int argc = 0;
        if (n > 0)
        {
            // 2.解析命令行
            argc = ParsingString();
        }
        else
            continue;
        if (argc == 0)
            continue;
        // 3.判断是否内建命令  //由bash本身执行的命令，不创建子进程
        n = isBuiltCommand(argv, argc);
        // 4.如果不是内建命令就是普通命令
        if (!n)
            normalCommand();
    }
    return 0;
}